Repository navigation
feat: term.update() folds CapabilityEvents into runtime capabilities - #133
Conversation
Changeset presentThis pull request is classified as breaking_change: @bomb.sh/tty |
|
Size Increased — +10.1 KB 122.6 KB unpacked |
commit: |
c64972d to
d5be56e
Compare
Widen Update from { width, height } | CapabilityEvent to
{ width, height } | InputEvent. Events other than resize and capability
are no-op steps, so hosts can pass the full scan() events array without
filtering. Only ResizeEvent carries width/height, so the shapes stay
unambiguous. Matches the implementation in #133.
42fa5a4 to
f124b5d
Compare
f124b5d to
5be02ae
Compare
5be02ae to
34fca15
Compare
eb3b262 to
3841243
Compare
|
Is the eventual plan to store the authoritative (mutable) copy of the terminfo in WASM? I don't think the spec has an opinion on this, but how do we anticipate using the capabilities snapshot when we render? If it is inside the WASM, the we should probably store it there and not copy it over every time. |
b3d41fb to
1999ea0
Compare
right, nothing actually reads capabilities yet, but it probably should end up in WASM once #60 lands. the API doesn't change, so I'd like to defer that decision to #60—left a note in the spec so we track it. |
cowboyd
left a comment
There was a problem hiding this comment.
awesome! sounds like a plan
1999ea0 to
50c99fc
Compare
…(renderer-spec §7.7, §8.6)
The terminfo spec no longer lists applyUpdate as public API; update() is the only entry point, and tests already exercise it through term.update().
The renderer spec now accepts any InputEvent as an Update and requires non-resize, non-capability events to change no state and emit no bytes.
Match createInput: the renderer option is `terminfo`, typed `TerminalInfo`. Adds the first test that seeds createTerm from a detected TerminalInfo (previously untested; test/caps.ts helpers were unused). The stack changeset now covers only what this PR and #131 add — detectTerminal/TerminalInfo, createTerm's option, term.capabilities, and the update() signature change — since #132 carries its own changeset for the input side.
Replace the `Update | readonly Update[]` overload with a single
`update(events: readonly InputEvent[])` signature (renderer-spec §7.7,
§8.6; terminfo-spec §10.4, §10.5).
ResizeEvent is already a member of InputEvent, so a separate `Update`
union adds nothing. Discriminating on `type` instead of a structural
`{ width, height }` shape makes every step a tagged variant. The host
loop collapses to passing `scan().events` straight through, and
out-of-band resizes (SIGWINCH) pass a constructed ResizeEvent.
The bare `{ width, height }` shape shipped in 0.9.0 is removed in the
same release that already breaks `update({ events })`, so callers
migrate once.
Implements the spec change in the previous commit. The `Update` type
and the single-or-array overload are gone; update() takes
`readonly InputEvent[]` and folds each event in order.
Steps now discriminate on `type`: `"resize"` resizes, `"capability"`
folds into RuntimeCapabilities, everything else is a no-op. That drops
the structural `"width" in` check and every cast in applyUpdate, since
CapabilityEvent narrows by `key`. A new test pins the tag-based
discrimination: an untagged `{ width, height }` no longer resizes.
validated() forwards the new signature unchanged.
The migration now covers both shapes 0.9.0 shipped in #113, `update({ events })` and `update({ width, height })`. Both become an array of InputEvents, with resizes expressed as `{ type: "resize", width, height }`.
Records the current design and the expected direction in §13, so the question of where the authoritative copy lives has an answer on record. Today RuntimeCapabilities lives only in the TypeScript Term closure: no renderer output reads it, so nothing crosses into WASM. Nearly every planned consumer (color encoding in #60, erase strategy from bce/autoMargin/xenl, sync-output wrapping) lives in C. The expected direction is to make WASM authoritative so render() reads capabilities without per-frame transfer. The public contract (update() folds, term.capabilities is a frozen snapshot) is the same either way, so the storage decision is deferred to #60, where the first consumer lands.
The TermOptions.terminfo docstring said createTerm "seeds the private TermInfo struct", but createTerm never touches a C struct: it seeds term.capabilities in TypeScript. The terminfo.h header described the #106 design, where the renderer read a shared struct and the input parser wrote probe responses into it. terminfo-spec §4.2 and TINV-6 replaced that: the two share no memory, and the struct is only used by detectTerminal(). The TERMINFO_DA1 comment named a queryTermInfo probe window that no longer exists. terminfo_confirm and TERMINFO_DA1 stay: if #60 makes WASM authoritative for capabilities (renderer-spec §13), they are the natural fold path.
50c99fc to
3272810
Compare
… them (renderer-spec §7.9)
Elements declare open(id, { pointerShape }). While
term.capabilities.pointerShape is true (raised by #132/#133 from the
probe's OSC 22 query reply), each render resolves the shape under the
pointer and appends `OSC 22 ; <shape> ST` to result.output when it
changes. The host keeps writing one buffer (cowboyd on #101). The
capability is the only gate; there is no createTerm option.
Everything lives in TypeScript; the wasm module and the packed encoding
are untouched, so there is no size or startup cost (#101 regressed
createTerm ~20% and grew the bundle 8.3 KB). Without the capability the
render path pays one boolean check and never reads pointerShape (a test
pins this with a counting getter). With it, the directive walk runs only
when the pointer is over something, and only a frame whose shape
changed copies output to append the sequence.
Resolution takes the last declaring id in Clay's pointer-over order:
pre-order within the topmost layer the pointer reaches, so the innermost
element wins and capture-mode floats hide what is beneath while
passthrough floats defer to it. snapshot() records its declared shapes
beside the packed bytes so pre-packed subtrees still participate.
Values outside the kitty/CSS vocabulary are ignored (and rejected by
validate()), so arbitrary strings never reach an OSC payload.
Restore (dreyfus92 on #101): set-only OSC 22 cannot pop, so the emitted
shape is the one piece of cross-frame state. It resets to "default"
when the pointer leaves or is omitted, and update() returns the reset
when the pointer-shape capability is withdrawn. Resize keeps it, since
the terminal's pointer did not change.
… them (renderer-spec §7.9)
Elements declare open(id, { pointerShape }). While
term.capabilities.pointerShape is true (raised by #132/#133 from the
probe's OSC 22 query reply), each render resolves the shape under the
pointer and appends `OSC 22 ; <shape> ST` to result.output when it
changes. The host keeps writing one buffer (cowboyd on #101). The
capability is the only gate; there is no createTerm option.
Everything lives in TypeScript; the wasm module and the packed encoding
are untouched, so there is no size or startup cost (#101 regressed
createTerm ~20% and grew the bundle 8.3 KB). Without the capability the
render path pays one boolean check and never reads pointerShape (a test
pins this with a counting getter). With it, the directive walk runs only
when the pointer is over something, and only a frame whose shape
changed copies output to append the sequence.
Resolution takes the last declaring id in Clay's pointer-over order:
pre-order within the topmost layer the pointer reaches, so the innermost
element wins and capture-mode floats hide what is beneath while
passthrough floats defer to it. snapshot() records its declared shapes
beside the packed bytes so pre-packed subtrees still participate.
Values outside the kitty/CSS vocabulary are ignored (and rejected by
validate()), so arbitrary strings never reach an OSC payload.
Restore (dreyfus92 on #101): set-only OSC 22 cannot pop, so the emitted
shape is the one piece of cross-frame state. It resets to "default"
when the pointer leaves or is omitted, and update() returns the reset
when the pointer-shape capability is withdrawn. Resize keeps it, since
the terminal's pointer did not change.
Part 4/4 (final) of the terminfo foundation stack. Requires #132.
term.update()is now the single entry point for resize and capability changes: pass an array ofInputEvents (resize is{ type: "resize", width, height }); it returns bytes to write (empty for now)createTerm({ terminfo })seeds the renderer from aTerminalInfo;term.capabilitiesexposes the current snapshot (256-color baseline when omitted)validated()forwards the newcapabilitiesgetterdetectTerminal/TerminalInfo, thecreateTermoption, and theupdate({ events })/update({ width, height })→update(events)breakSplit from #106 for reviewability; #106 closes when this stack lands.